
#ifndef XDNS_H
#define XDNS_H

#include <netinet/in.h>
#include <sys/select.h>
#include <string>
#include <vector>

enum XdnsError
{
    XE_Ok = 0,
    XE_BadResponse,             /*!< Bad response. */
    XE_Inval,                   /*!< Invalid argument. */
    XE_Network,                 /*!< Generic network error. */
    XE_NoEnt,                   /*!< No such entry. */
    XE_NoMem,                   /*!< Out of memory. */
    XE_NoSupport,               /*!< Not supported. */
    XE_Range,                   /*!< Size out of range. */
    XE_Unexpected,              /*!< An unexpected case occurred. */

    // DNS special errors.
    XE_UdpTruncated = 100,      /*!< A UDP reply is truncated. */
    XE_RcodeFormatError,        /*!< The name server was unable to
                                     interpret the query.
                                */
    XE_RcodeServerFailure,      /*!< The name server was unable to
                                     process this query due to a
                                     problem with the name server.
                                */
    XE_RcodeNameError,          /*!< Meaningful only for responses
                                     from an authoritative name server,
                                     this code signifies that the
                                     domain name referenced in the
                                     query does not exist.
                                */
    XE_RcodeNotImplemented,     /*!< The name server does not support
                                     the requested kind of query.
                                */
    XE_RcodeRefused             /*!< The name server refuses to perform
                                     the specified operation for policy
                                     reasons.
                                */
};

class XdnsConfig
{
public:
    /*! XdnsConfig contructor.

      @param serverPort DNS server port for both TCP and UDP. It should
                        follow the host byte order.
      @param serverIpAddress A string of DNS server IP address.
    */
    XdnsConfig(unsigned short serverPort,
               const std::string& serverIpAddress);
    XdnsConfig(const XdnsConfig& anotherConfig);
    ~XdnsConfig();
    XdnsConfig& operator=(const XdnsConfig& anotherConfig);

    unsigned short m_serverPort;
    std::string m_serverIpAddress;
};

/*! The host entry is decoded from the DNS response. */
class XdnsHostEntry
{
public:
    XdnsHostEntry();
    virtual ~XdnsHostEntry();

    /*! Retrieve the official hostname.

        @return String of the hostname.
    */
    virtual std::string GetHostname() const = 0;

    /*! Retrieve the internet host address.

        @return Internet host address.
    */
    virtual const struct in_addr* GetAddress() const = 0;
};

class XdnsListener
{
public:
    XdnsListener();
    virtual ~XdnsListener();

    /*! Callback function for requesting the host.

        This callback function is called when user calls the functions
        GetHostByName or GetHostByAddress of class XdnsHandler.

        @param queryId Query ID identified which query is requested, the
                       query ID is returned when the requesting function is
                       called.
        @param error XE_Ok if success.
        @param hostEntry On return the host entries which are decoded from
                         the DNS response if success, NULL otherwise.
        @return None.
    */
    virtual void GetHostCallback(long queryId,
                                 XdnsError error,
                                 const std::vector<XdnsHostEntry*>* hostEntries
                                 ) = 0; 
};

class XdnsHandler
{
public:
    XdnsHandler();
    virtual ~XdnsHandler();

    /*! Initialize a new XDNS handler.

        @param config XDNS configuration.
        @param handler On return the new XDNS handler, it must be destroyed by
                       calling the function Release when it is useless.
        @param errorDescription On return the string described the error if an
                                error occured. It could be NULL if user did
                                not care the error description.
        @return XE_Ok if success.
    */
    static XdnsError Init(const XdnsConfig& config,
                          XdnsHandler** handler,
                          std::string* errorDescription);

    static void Release(XdnsHandler* handler);

    /*! Request the host by a name.

        This function is the asynchronous implementation of the system function
        gethostbyname.

        @param name Hostname used to request.
        @param listener The function GetHostCallback of this listener is called
                        when requesting is complete. It could be NULL if user
                        did not care about the requesting result.
        @param queryId On return the query ID if success. It could be NULL if
                       user did not care about it.
        @return XE_Ok if success.
    */
    virtual XdnsError GetHostByName(const std::string& name,
                                    XdnsListener* listener,
                                    long* queryId
                                    ) = 0;

    /*! Request the host by an address.

        This function is the asynchronous implementation of the system function
        gethostbyaddr.

        @param ipAddress IP address used to request.
        @param listener The function GetHostCallback of this listener is called
                        when requesting is complete. It could be NULL if user
                        did not care about the requesting result.
        @param queryId On return the query ID if success. It could be NULL if
                       user did not care about it.
        @return XE_Ok if success.
    */
    virtual XdnsError GetHostByAddress(const struct in_addr* ipAddress,
                                       XdnsListener* listener,
                                       long* queryId
                                       ) = 0;

    /*! Cancel query by a query ID.

        @param queryId Query ID identified which query should be canceled. 
        @return None.
    */
    virtual void CancelQuery(long queryId) = 0;

    /*! Retrieve the processing set of file descriptors to select.

        @param nfds On return One greater than the number of the highest socket
                    set in either readfds or writefds.
        @param readfds On return reading file descriptors.
        @param writefds On return writing file descriptors.
        @return XE_Ok if success,
                XE_NoEnt if no file descriptors is being processed.
    */
    virtual XdnsError GetProcessingFds(int* nfds,
                                       fd_set* readfds,
                                       fd_set* writefds
                                       ) = 0;

    /*! Process the activated file descriptors.

        This function should be called after calling the function select on the
        processing file descriptors, they are retrieved from the function
        GetProcessingFds.

        @param readfds Reading file descriptors used to process.
        @param writefds Writing file descriptors used to process.
        @return XE_Ok if success.
    */
    virtual XdnsError Process(fd_set* readfds,
                              fd_set* writefds
                              ) = 0;

    /*! Retrieve the description of last occured error.

        @return String described the last error. 
    */
    virtual std::string GetLastErrorDescription() const = 0;
};

#endif  // XDNS_H

